package com.se.invigilation.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.se.invigilation.component.JWTComponent;
import com.se.invigilation.component.LTokenComponent;
import com.se.invigilation.dox.User;
import com.se.invigilation.dto.DepartmentDTO;
import com.se.invigilation.exception.Code;
import com.se.invigilation.exception.XException;
import com.se.invigilation.service.CommonService;
import com.se.invigilation.vo.RequestConstant;
import com.se.invigilation.vo.ResultVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.Optional;

@RestController
@RequestMapping("/api/")
@Slf4j
@RequiredArgsConstructor
public class LoginController {
    private final CommonService commonService;
    private final PasswordEncoder encoder;
    private final JWTComponent jwtComponent;
    private final ObjectMapper objectMapper;
    private final LTokenComponent lTokenComponent;

    @PostMapping("login")
    public Mono<ResultVO> login(@RequestBody Map<String, String> map, ServerHttpResponse response) {
        String account = map.get("account");
        String password = map.get("password");
        String ltoken = map.get("ltoken");
        return commonService.getUser(account)
                .filter((u) -> encoder.matches(password, u.getPassword()))
                .map((u) -> {
                    setToken(response, u);
                    if (ltoken != null) {
                        response.getHeaders().add("ltoken", lTokenComponent.encode(u.getAccount()));
                    }
                    return ResultVO.success(u);
                }).defaultIfEmpty(ResultVO.error(Code.LOGIN_ERROR));
    }

    @GetMapping("l-login")
    public Mono<ResultVO> login(@RequestHeader Optional<String> ltoken,
                                ServerHttpResponse response) {
        return ltoken.map(s -> lTokenComponent.decode(s)
                        .flatMap(commonService::getUser)
                        .map(u -> {
                            setToken(response, u);
                            return ResultVO.success(u);
                        }))
                .orElseGet(() -> Mono.just(ResultVO.error(Code.LOGIN_TOKEN_ERROR)));

    }

    private void setToken(ServerHttpResponse resp, User user) {
        try {
            DepartmentDTO dd = objectMapper.readValue(user.getDepartment(), DepartmentDTO.class);
            Map<String, Object> tokenM = Map.of(RequestConstant.UID, user.getId(),
                    RequestConstant.ROLE, user.getRole(),
                    RequestConstant.COLLID, dd.getCollId(),
                    RequestConstant.DEPID, dd.getDepId());
            String token = jwtComponent.encode(tokenM);
            resp.getHeaders().add("token", token);
            resp.getHeaders().add("role", user.getRole());
        } catch (JsonProcessingException var6) {
            throw XException.builder().code(Code.LOGIN_ERROR).build();
        }
    }
}
